﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#if Gdmp || Gnuf
using Glodon.Gdmp.DB;
#elif Gap
using Glodon.Gap.DB;
using Glodon.Gap.UI;
#endif
using Glodon.Lookup.Core;
using Glodon.Lookup.Core.ModelBase;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Enums;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Objects;

using System.Collections.ObjectModel;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using System.Windows.Data;
using System.ComponentModel;

using Glodon.Lookup.ViewModels.Utils;
using Glodon.Lookup.Utils;

namespace Glodon.Lookup.ViewModels.Pages.Tracebale
{
    public partial class SnoopHistoryViewModel : ObservableObject, ITraceableViewModel, ITabViewModel
    {
        public bool NeedReSelect;
        public SearchBoxMediator SearchBoxMediator { get; set; }

        public SnoopHistoryViewModel(SearchBoxMediator mediator)
        {
            SearchBoxMediator = mediator;
            SnoopableViewSource = new CollectionViewSource();
            SnoopableViewSource.GroupDescriptions.Add(new PropertyGroupDescription("Descriptor.Type"));
            SnoopableViewSource.Filter += SnoopableViewSource_Filter;
            PropertyChanged += SnoopHistoryViewModel_PropertyChanged;
        }
        private void SnoopableViewSource_Filter(object sender, FilterEventArgs e)
        {
            e.Accepted = false;
            if (string.IsNullOrWhiteSpace(SearchBoxMediator.SearchText))
            {
                e.Accepted = true;
                return;
            }
            var snoopableObject = e.Item as SnoopableObject;
            if (snoopableObject!.Descriptor.Name.IndexOf(SearchBoxMediator.SearchText, StringComparison.OrdinalIgnoreCase) >= 0)
                e.Accepted = true;
            else if (snoopableObject.Descriptor.Type.IndexOf(SearchBoxMediator.SearchText, StringComparison.OrdinalIgnoreCase) >= 0)
                e.Accepted = true;
            else if (SearchEngine.MatchId(snoopableObject, SearchBoxMediator.SearchText))
            {
                e.Accepted = true;
            }
            if (snoopableObject is { IsSelected: true } && e.Accepted == false)
            {
                //因为对象即将被过滤，先取消选中
                snoopableObject.IsSelected = false;
                //将过滤前选中对象保存起来，如果过滤后结果为空，需要恢复过滤前的选中项，避免历史地址栏中当前选中信息丢失
                SnoopHistory.Last().CachedSelectedObject = snoopableObject;
                NeedReSelect = true;
            }
            return;
        }
        private void SnoopableObjects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            SnoopableViewSource.View.Refresh();
        }
        public void TrySelectFirst(bool isInitial)
        {
            if (NeedReSelect || isInitial)
            {
                SnoopableObject? cachedSelectedObject = SnoopHistory.Last().CachedSelectedObject;
                if (SnoopableViewSource is { View: { } view } && view is CollectionView { Count: 0 })
                {
                    //过滤结果为空，且过滤前选中项还存在于当前Tab数据中（有可能因为视图改变或者文档改变等因素，之前选中的对象已经不存在于当前数据中）
                    //恢复过滤前的选中项，以避免历史地址栏中当前选中信息丢失
                    if (SnoopableObjects.Contains(cachedSelectedObject!))
                    {
                        if (cachedSelectedObject != null)
                        {
                            cachedSelectedObject.IsSelected = true;
                        }
                        SnoopableData = Array.Empty<Descriptor>();
                        NeedReSelect = false;
                        return;
                    }
                }
                if (SnoopableViewSource is { View: { Groups: { Count: > 0 } } collecView })
                {
                    //如果当前过滤结果有选中项，直接返回
                    IEnumerable<SnoopableObject> filtered = collecView.Cast<SnoopableObject>();
                    if (filtered.Any(s => s.IsSelected) || SnoopHistory is { Count: > 0 } && filtered.Contains(cachedSelectedObject))
                    {
                        if (cachedSelectedObject != null)
                        {
                            cachedSelectedObject.IsSelected = true;
                        }
                        return;
                    }

                    var firstGroup = SnoopableViewSource.View.Groups.First() as CollectionViewGroup;
                    if (firstGroup != null && firstGroup.Items.Any())
                    {
                        SnoopableObject? snoopableObject = firstGroup.Items.First() as SnoopableObject;
                        if (snoopableObject is not null)
                        {
                            snoopableObject.IsSelected = true;
                            NeedReSelect = false;
                        }
                    }
                }
            }
        }
        private Comparer<object> _snoopableListComparer;
        private void UpdatehViewSource()
        {
            SnoopableViewSource.Source = SnoopableObjects;
            _snoopableListComparer = Comparer<object>.Create((x, y) =>
            {
                try
                {
                    if (x is not SnoopableObject xs || y is not SnoopableObject ys)
                    {
                        throw new ArgumentException($"ListCollectionView自定义排序只支持{nameof(SnoopableObject)}");
                    }
                    if (IsCurrentTabObject(xs, Tab) && IsCurrentTabObject(ys, Tab))
                    {
                        return 0;
                    }
                    if (IsCurrentTabObject(xs, Tab))
                    {
                        return -1;
                    }
                    else if (IsCurrentTabObject(ys, Tab))
                    {
                        return 1;
                    }
                    else
                    {
                        if (xs.Descriptor.Type.CompareTo(ys.Descriptor.Type) != 0)
                        {
                            return xs.Descriptor.Type.CompareTo(ys.Descriptor.Type);
                        }
                        if (xs.Descriptor.Name != null && xs.Descriptor.Name.CompareTo(ys.Descriptor.Name) != 0)
                        {
                            return xs.Descriptor.Name.CompareTo(ys.Descriptor.Name);
                        }
                        if (xs.Descriptor.Description != null && xs.Descriptor.Description.CompareTo(ys.Descriptor.Description) != 0)
                        {
                            return xs.Descriptor.Description.CompareTo(ys.Descriptor.Description);
                        }
                        else
                        {
                            if (xs.Descriptor.Description != null)
                            {
                                return -1;
                            }
                            if (ys.Descriptor.Description != null)
                            {
                                return 1;
                            }
                        }
                    }
                    return 0;
                }
                catch (Exception e)
                {
                    ExceptionUtils.ShowExceptionMessage(e);
                    return 0;
                }
            });
            //对分组进行自定义排序
            (SnoopableViewSource.View as ListCollectionView)!.CustomSort = _snoopableListComparer;
            if (SnoopHistory.Last().SelectedObject is null && SnoopHistory.Last().CachedSelectedObject is null)
            {
                var snoopableList = SnoopableObjects.ToList();
                snoopableList.Sort(_snoopableListComparer);
                var firstObj = snoopableList.FirstOrDefault();
                if (firstObj != null)
                {
                    //默认选中第一项
                    firstObj.IsSelected = true;
                }
            }
        }
        private void SnoopHistoryViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals(nameof(SnoopableObjects)))
            {
                if (SnoopableObjects is { Count: > 0 })
                {
                    SnoopableObjects.CollectionChanged += SnoopableObjects_CollectionChanged;
                    UpdatehViewSource();
                    TrySelectFirst(true);
                }
            };
        }

        public DataTabType Tab { get; set; }

        public ObservableCollection<SnoopAction> SnoopHistory { get; set; } = new ObservableCollection<SnoopAction>();

        public ObservableCollection<SnoopableObject> SnoopableObjects
        {
            get
            {
                if (SnoopHistory.Count > 0)
                {
                    return new ObservableCollection<SnoopableObject>(SnoopHistory.Last().Objects);
                }
                return null;
            }
        }

        public CollectionViewSource SnoopableViewSource { get; private set; }

        [ObservableProperty]
        private IReadOnlyCollection<Descriptor> _snoopableData;

        [ObservableProperty]
        private int _historyLimit = 20;
        private bool IsCurrentTabObject(SnoopableObject snoopableObject, DataTabType dataTab)
        {
            bool isTabType = false;
            switch (dataTab)
            {
                case DataTabType.Document:
                    isTabType = snoopableObject.Object is Document;
                    break;
                case DataTabType.ModelView:
                    isTabType = snoopableObject.Object is ModelView;
                    break;
                default:
                    break;
            }
            return isTabType && (dataTab == DataTabType.Document || dataTab == DataTabType.ModelView);
        }

        public void AddHistory(IReadOnlyCollection<SnoopableObject> historyObjects)
        {

            if (SnoopHistory != null)
            {
                if (SnoopHistory.Count == HistoryLimit)
                {
                    //移除第二层历史（首页后的第一次Snoop）
                    SnoopHistory.RemoveAt(1);
                }
                SnoopAction action = new SnoopAction();
                action.Objects = historyObjects;
                SnoopHistory.Add(action);
                OnPropertyChanged(nameof(SnoopableObjects));
            }
        }

        [RelayCommand]
        private void BackForwardHistory()
        {
            if (SnoopHistory is { Count: > 1 })
            {
                SnoopHistory.RemoveAt(SnoopHistory.Count - 1);
                OnPropertyChanged(nameof(SnoopableObjects));
            }
        }

        public void ClearHistory()
        {
            SnoopHistory?.Clear();
        }

        [RelayCommand]
        private void ResetHistory(object parameter)
        {
            int from = -1;
            if (parameter is int)
            {
                from = (int)parameter;
                if (from < 0 || from >= SnoopHistory.Count)
                {
                    return;
                }
            }
            else if (parameter is SnoopAction item)
            {
                from = SnoopHistory.IndexOf(item) + 1;
            }
            if (from > -1 && SnoopHistory.Count > from)
            {
                int count = SnoopHistory.Count;
                //移除从指定位置起到最后的全部元素
                //（循环count-from次，每次都移除索引为from的元素）
                for (int i = from; i < count; i++)
                {
                    SnoopHistory.RemoveAt(from);
                }
                OnPropertyChanged(nameof(SnoopableObjects));
                //如果过滤后树中结果为空，右侧表格置空
                if (SnoopableViewSource is { View: { } view } && view is CollectionView { Count: 0 })
                {
                    SnoopableData = Array.Empty<Descriptor>();
                }

            }
        }

        /// <summary>
        /// 刷新绑定了最新历史数据的视图，供外部手动更新数据后调用
        /// </summary>
        public void RaiseSnoopableObjectsChanged()
        {
            OnPropertyChanged(nameof(SnoopableObjects));
        }
        /// <summary>
        /// 重新查询当前页数据，并对选中项进行恢复
        /// </summary>
        public void UpdateHome()
        {
            try
            {
                if (SnoopHistory is not { Count: > 0 })
                {
                    return;
                }
                IReadOnlyCollection<SnoopableObject> newObjects = Selector.Snoop(Tab);
                IReadOnlyCollection<SnoopableObject> objects = SnoopHistory.First().Objects;
                SnoopableObject? selectedOriginal = objects.Any(o => o.IsSelected) ? objects.First(o => o.IsSelected) : null;
                foreach (var newSnoopObj in newObjects)
                {
                    //SnoopableObject 更新了，但其内部对象 SnoopableObject.Object 可能是刷新前已有的实例，所以：

                    //如果当前 SnoopableObject 的内部对象与上次选中的 SnoopableObject 的内部对象相等，可以将当前SnoopableObject设为选中项
                    //（即：更新 SnoopableObject 集合时尽可能保持更新前的选中项）
                    if (SnoopHistory != null && SnoopHistory.Any())
                    {
                        if (selectedOriginal is { Object: { } } && newSnoopObj.Object.Equals(selectedOriginal.Object))
                        {
                            newSnoopObj.IsSelected = true;
                        }
                        else
                        {
                            //如果当前 SnoopableObject 的内部对象与上次选中的 SnoopableObject 的内部对象都是元素类型且具有相同元素ID，同上
                            if (newSnoopObj.Object is Element newElement && selectedOriginal is { Object: Element selElementOriginal })
                            {
                                ElementId originalSelId = null;
                                try
                                {
                                    originalSelId = selElementOriginal.Id;
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    bool valid = selElementOriginal.IsValidObject();
                                    if (valid && newElement.Id.Equals(selElementOriginal?.Id))
                                    {
                                        newSnoopObj.IsSelected = true;
                                    }
                                    //当旧元素无效，但其内部数据依然正常（sdk bug?）时，忽略无效状态
                                    else if (!valid && originalSelId != null && originalSelId.Equals(newElement.Id))
                                    {
                                        newSnoopObj.IsSelected = true;
                                    }
                                }
                                catch (Exception e)
                                {
                                    ExceptionUtils.ShowExceptionMessage(e);
                                }
                            }
                        }
                    }
                }
                if (SnoopHistory is { Count: > 0 })
                {
                    if (selectedOriginal is { Object: Element selElementOriginal } && !selElementOriginal.IsValidObject())
                    {
                        ResetHistoryCommand.Execute(1);
                    }
                    SnoopHistory.First().Objects = newObjects;
                    if (SnoopHistory.Count == 1)
                    {
                        OnPropertyChanged(nameof(SnoopableObjects));
                    }
                    SnoopHistory.First().RaiseSelectedObjectChanged();
                }
            }
            catch (Exception e)
            {
                ExceptionUtils.ShowExceptionMessage(e);
            }
        }

    }
}
